/*====================================================================================
# «ДЭНСИ:РМК» module of «ДЭНСИ: КАССА» complex is the front-office application for POS-systems based on fiscal registrators.
# © OOO "DNC Trade" 2009, Authors: Savitskaya Alexandra, Pavlova Irina(Pavlova_Irina@shtrih-s.ru), Sukhanova Anna (anuta.s13@gmail.com), Savrasov Alexandr.
# This is a FREE software, licensed under GNU/GPL terms. See details in LICENSING_ENG.TXT in main directory
#====================================================================================
# Модуль «ДЭНСИ:РМК» программного комплекса «ДЭНСИ: КАССА» - рабочее место кассира для POS-систем на базе фискальных регистраторов
# © ООО "ДЭНСИ Трейд" 2009, Авторы: Савицкая Александра, Павлова Ирина(Pavlova_Irina@shtrih-s.ru), Суханова Анна (anuta.s13@gmail.com), Саврасов Александр.
# Данная программа является свободным программным обеспечением, выпускается на условиях Стандартной Общественной Лицензии GNU/GPL
# Подробнее смотрите в файле LICENSING_RUS.TXT в основной директории
#====================================================================================*/

#include "receipt.h"


#include <sys/time.h>
#include <string.h>
#include <stdlib.h>
//#include <malloc.h>
#include <math.h>
#include <stdio.h>
#include "addon_func.h"

double MaxDiscountAndCharge(double s , vector<Discount>&discount);
double Max_Charge(double s , vector<Discount> discount);
double Max_Discount(double s, vector<Discount>  discount);


SP * sp_receipt;


//==================================================================================//
Receipt::Receipt(SP * sp_rec)
{
//puts("Receipt::Receipt(Sp * sp_rec)");
  record_num = 0;
  receipt_num = -1;
  sum = 0;
  dsum = 0;
  

  discount.resize(4);

  sp_receipt = sp_rec;
  
  receipt_type = CHECK_FREE;
  receipt_number = 0;
  
  round_sum = 0;
  
  closed = false;
}

//==================================================================================//
Receipt::Receipt(const Receipt & r)
{ //   int size;
	  
  closed = r.closed;
  record_num = r.record_num;
  receipt_num = r.receipt_num;
  sum = r.sum;
  dsum = r.dsum;
  date = r.date;
  cashier = r.cashier;
  
  doc_number = r.doc_number;
  kkm_serial = r.kkm_serial;
  n_session = r.n_session;	
  
  receipt_type = CHECK_FREE;
  receipt_number = 0;  
  
  discount.assign(r.discount.begin(), r.discount.end());
  records.assign(r.records.begin(), r.records.end());
  positiontaxes.assign(r.positiontaxes.begin(), r.positiontaxes.end());  
  
  round_sum = 0;
}
//==================================================================================// 
Receipt::~Receipt()
{
    
}
//==================================================================================// 
void Receipt::setDate(const char* d)
{
    date = d;
}
//==================================================================================// 
const char* Receipt::getDate()
{
    return date.c_str();
}
//==================================================================================// 
ReceiptType Receipt::GetCheckType()
{
    return receipt_type;
}
//==================================================================================//    
void Receipt::SetCheckType(ReceiptType rt)
{
    receipt_type = rt;
}
//==================================================================================//    
int Receipt::GetCheckNumber()
{
    return receipt_number;
}
//==================================================================================// 
void Receipt::SetCheckNumber(int check_number)
{
    receipt_number = check_number;
}
//==================================================================================//
Discount * Receipt::GetDiscountElemRef(int index)
{
//puts("Discount * Receipt::GetDiscountElemRef(int index)");
  return &discount.at(index);
}

//==================================================================================// 
Record_Receipt * Receipt::GetPosElemRef(int index)
{
//puts("Record_Receipt * Receipt::GetPosElemRef(int index)");
  return &records.at(index);
}
    
//==================================================================================// 
PositionTaxes * Receipt::GetTaxElemRef(int index)
{
//puts("PositionTaxes * Receipt::GetTaxElemRef(int index)");
   //cout << "positiontaxes.at(index). " << positiontaxes.at(index).tax_group_id << positiontaxes.at(index).tax_group_name << endl;
 return &positiontaxes.at(index);
}


//==================================================================================//
void Receipt::CloseReceipt()
{
//puts("void Receipt::CloseReceipt()");
  closed = true;
}

//==================================================================================//
bool Receipt::GetStateofClosing()
{
//puts("bool Receipt::GetStateofClosing()");
  return closed;
}

//==================================================================================//
//flagcode = true - поиск товара по внутреннем коду, flagcode = false - поиск товара по штрих-коду  
int Receipt::FindEquilPosition(const char *code, bool flagcode)
{
//puts("Receipt::FindEquilPosition(const char *code, bool flagcode)");
  int pos = -1;
  string code_string = code;
  for(int i = 0; i < (int)records.size(); i++)
  {
    string code_str = records.at(i).GetCode();
    string bar_str = records.at(i).GetBarcode();
    if((flagcode && (code_string == code_str)) || (code_string == bar_str))
    {
      if(records.at(i).enable_flag)
      {
        pos = i;
        i = record_num;
      }
    }
  }
  return pos;
}
//==================================================================================//
//flagcode = true - поиск товара по внутреннем коду, flagcode = false - поиск товара по штрих-коду  
double Receipt::QuantityWareByCode(const char* acode, bool flagcode)
{
  double q = 0;
  string code = acode;
  string str; 

  for(int i = 0; i < (int)records.size(); i++)  {
	if (flagcode) str = records.at(i).GetCode(); else str = records.at(i).GetBarcode();
    	if ( code == str ) q = q + records.at(i).GetQuantity();
  }

  return q;
}
//==================================================================================//
double Receipt::CalcPositionDiscounts()
{
//puts("Receipt::CalcPositionDiscounts()");
  double redsum = 0;
  for(vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++)
  {
    if(i->enable_flag)
      redsum = redsum + i->GetDSum();
  }
  printf("redsum = %f \n", redsum);
  return redsum;
}
//==================================================================================//
double Receipt::RecalculationDiscounts()
{
//puts("Receipt::RecalculationDiscounts()");

  for(vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++)
	  i->RecalculationDiscounts();

  dsum = 0;
  double s;
  if(sp_receipt->disc_exception_mode)
    s = ExceptionSum();
  else
    s = Sum();         
    
  dsum = 0;  
  switch(sp_receipt->result_discount) // Правило выбора начисления итоговой скидки
  {
    case 0 : // выбирается максимальная скидка
      dsum = MaxDiscountAndCharge(s, discount);
      break;
    case 1 : // выбирается объединение скидок (скидки начисляются последовательно, используя порядок начисления)
      dsum = Union_Discount(s, discount);
      break;
    case 2 : // скидки суммируются, а потом начисляются
      dsum = Sum_Discount(s, discount);	
      break;
  }
  
  
  //double result_check_sum = Sum() - dsum;
  //printf("getResult Sum = %f \n", result_check_sum);
  
  return dsum;
}

//==================================================================================//
double Receipt::CalculationPositionTaxBase(int i)
{
    
    double sum = records.at(i).GetSum() - records.at(i).GetDSum();
    double base_sum = sum;
    
    double prestage_percent = 0;
    bool percent_flag = false;
    
    // Посчитаем налоги 
    
    for (vector<TaxRate>::iterator j = positiontaxes.at(i).taxrates.begin(); j != positiontaxes.at(i).taxrates.end(); j ++)  {
	
	if ( !j->change_base ) {
	    j->taxsum = sum *(prestage_percent + j->rate) / 100;
	    base_sum -=  j->taxsum;
	} else {
	    j->taxsum = base_sum *(prestage_percent + j->rate) / 100;
	    base_sum -=  j->taxsum;
	}
    }
    
    return base_sum;
}

//==================================================================================// 
void Receipt::Add_Record(const char *barcode, const char *code, const char *shortcut, const char *longtitle, double quantity, double price, int fls, bool cfl, int a_sch, int f_sch, int t_gr, double m_pr, int op_id, const char *acc, double f)
{
printf("Receipt::Add_Record(%s, %s, %s, %f, %f)\n", barcode, code, longtitle, quantity, price);

  if (receipt_type == CHECK_EXCHANGE && records.size() == 0) {
    price = -price;
  }

  Record_Receipt record;

  record_num++;
  sum = sum + quantity * price;

  record.Add_Record_Receipt(barcode, code, shortcut, longtitle, quantity, price, fls, cfl, a_sch, f_sch, t_gr, m_pr, op_id, acc, f);
  //puts("Before Push");
  records.push_back(record);
  //puts("After Push");
}

//==================================================================================// 
//n - количество налоговых ставок группы
void Receipt::Add_Taxes_Info(int tax_group, const char* tg_name,  int n)
{
    //cout << "add_taxes_ifno (" << tax_group << ", " << tg_name << ", " << n << ")";
    
    PositionTaxes pt;
    
    pt.tax_group_id = tax_group;
    pt.SetName( string(tg_name) );
    pt.num_rates = n;
    
    positiontaxes.push_back( pt);
    
}
//======================================================================//
double Receipt::TestMinPrice(double procent) {
        // проверка мин цены 
    //puts("   test min price");
    //printf("TestMinPrice(double procent = %f) \n", procent);
    //printf("sp_receipt->control_min_price = %d \n", sp_receipt->control_min_price);
    //printf("sum of check = %f \n", sum);
    
    double check_sum = 0;
    
    if ( sp_receipt->control_min_price == 1 || sp_receipt->control_min_price == 2 ) {
	// вычислим правильный процент скидки
	double right_discount_sum = 0;
	bool error_min_price = false;
	procent = procent / 100;
	for (int i = 0; i < records.size(); i++) {
	    if ( records[i].enable_flag ) {
		if ( !sp_receipt->disc_exception_mode || ( sp_receipt->disc_exception_mode && records[i].GetDSum() == 0) ) {
		    double position_sum = records[i].GetSum() - records[i].GetDSum();
		    check_sum += position_sum;
		    double position_sum_with_procent = (1-procent)*position_sum;
		    double min_position_sum = records[i].min_price * records[i].GetQuantity();
		    //printf("   pos %d position sum = %f position_sum_with_procent = %f min_position_sum = %f \n", i, position_sum, position_sum_with_procent, min_position_sum);
		    //printf("position_sum = %f position_sum_with_procent  = %f min_position_sum = %f \n", position_sum, position_sum_with_procent, min_position_sum);  
		    if ( position_sum_with_procent < min_position_sum ) {
			error_min_price = true;
			right_discount_sum += position_sum - min_position_sum;
			//printf("   result position discount sum = %f \n", position_sum - min_position_sum);
		    } else {
			right_discount_sum += position_sum * procent;
			//printf("   result position discount sum = %f \n", position_sum * procent);
		    }
		}
	    }
	}
	
	//printf("   result check discount sum = %f \n", right_discount_sum);
	//printf("   result check discount percent = %f \n", (right_discount_sum/check_sum)*100);
	if ( !error_min_price ) return procent*100;
	if ( sp_receipt->control_min_price == 1 ) return -1;
	if ( sp_receipt->control_min_price == 2 ) return (right_discount_sum/check_sum)*100;
    }
    //printf("procent = %f \n", procent);
    return procent;
}
//======================================================================//
int Receipt::Add_Percent_Discount(int i, double p, const char * code, const char *name, const char *shortcut, bool manual_fl)
{
    //puts("RegistrationForm::Add_Percent_Discount(int i, double p, cosnt char * code, const char *name, const char *shortcut, bool manual_fl)");
    //printf("code of discount = %s \n", code);
    
    //printf("check discount = %f \n", p);
    
    printf("Add percent check discount - %f \n", p);
    
    double right_procent = TestMinPrice(p);
    //printf(" result check discount percent = %f \n", right_procent);
    
    //printf("right_procent = %f \n", right_procent);
    
    if ( right_procent == -1 ) return 4;
    
    discount.at(i).Add_Discount(0, right_procent);
    
    int ret = 0;
    double rec_sum = Sum();
    double rec_dsum = RecalculationDiscounts();    
    double s = discount.at(i).dsum;   
    
    //  проверка максимальной скидки
    if ( manual_fl && (s > 0) && (Round(rec_sum * sp_receipt->max_check_discount) / 100 < s) )      ret = 1;
    // проверка максимальной надбавки
    else if ( manual_fl && (s < 0) && (Round(rec_sum * sp_receipt->max_check_increase) / 100 < s) )   ret = 2; 
    else if ( rec_sum - rec_dsum <= 0 )    ret = 3;    
    
    //printf("ret = %d \n", ret);
    
    if ( ret != 0 )  {
	discount.at(i).Restore_Discount();
	RecalculationDiscounts();
    }  else  {
	discount.at(i).Set_Names(name, shortcut);
	//puts("set code for discount");
	discount.at(i).Set_Code(code);
    }
    return ret;
}

//======================================================================//
int Receipt::Add_Sum_Discount(int i, double s, const char *code, const char *name, const char *shortcut, bool manual_fl)
{
//puts("int Receipt::Add_Sum_Discount(int i, double s, const char *code, const char *name, const char *shortcut, bool manual_fl)");

    double rec_sum = Sum(); 
    
    printf("Add sum check discount - %f ", s);
    printf("percent = %f \n", s/rec_sum*100);
    
    double right_procent = TestMinPrice(s/rec_sum*100);
    //printf(" result check discount percent = %f", right_procent);
    //printf("right_procent = %f \n", right_procent);
    
    if ( right_procent == -1 ) return 4;
    
    double right_sum = right_procent*rec_sum/100;
    //printf("right_sum = %f \n", right_sum);
    //printf(" sum = %f \n", right_sum);
    
  discount.at(i).Add_Discount(right_sum, 0);
  
  int ret = 0;

  double rec_dsum = RecalculationDiscounts();
  
  if(manual_fl && (s > 0) && (Round(rec_sum * sp_receipt->max_check_discount) / 100 < s))   //  проверка максимальной скидки
    ret = 1; 
  else if(manual_fl && (s < 0) && (Round(rec_sum * sp_receipt->max_check_increase) / 100 < s)) // проверка максимальной надбавки
    ret = 2;
  else if(rec_sum - rec_dsum <= 0)
    ret = 3;
  
  if(ret != 0)
  {
    discount.at(i).Restore_Discount();
    RecalculationDiscounts();
  }
  else
  {
    discount.at(i).Set_Names(name, shortcut);
    discount.at(i).Set_Code(code);
  }
  return ret;
}


//==================================================================================// 
void Receipt::Clear_Receipt()
{
//puts("Receipt::Clear_Receipt()");
  if(!records.empty()) records.clear();
  if(!positiontaxes.empty())positiontaxes.clear();
  if(!discount.empty())discount.clear();

  record_num = 0;
  receipt_num = -1;
  sum = 0;
  dsum = 0;
  
  discount. resize(4);
  closed = false;
}    

//==================================================================================// 
void Receipt::Clear_Taxes()
{
  if(!positiontaxes.empty())positiontaxes.clear();
}

//==================================================================================// 
double Receipt::ExceptionSum()
{
//puts("Receipt::ExceptionSum()");
  double s = 0;
  
  for(vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++)
    if(i->enable_flag)
    {
      if(i->GetDSum() == 0)
      {
        s = s + i->GetSum();
      }
    }
  return s;
}

//==================================================================================// 
double Receipt::Sum()
{
//puts("Receipt::Sum()");
  double s = 0;
  for(vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++)
  {
    if(i->enable_flag)
    {
      s = s + i->GetSum() - i->GetDSum();
    }
 }
// Round added by A.S. 2008-02-07
//s = s - dsum;
  sum = Round(s*100)/100;
  return sum;
}
//==================================================================================// 
// возвращает сумму чека со скидками, налогами, с учетом удаленных (сторнированных) позиций
double Receipt::GetResultSum()
{
    //printf("Receipt::GetResultSum() \n");
   double s = 0;
   for ( vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++ )  {
     if ( i->enable_flag )    {
    	 s = s + i->GetSum() - i->GetDSum();
     }
   }
	s = s - dsum;
   
   sum = RoundCheckSum(s);
   
   //sum = Round(s*100)/100;
   
   printf("GetResultSum return = %f \n", sum);
	return sum;
}	
//==================================================================================// 
double Receipt::RoundCheckSum(double check_sum) {
    
    //printf("RoundCheckSum(check_sum = %f, round_power = %f, type) \n", check_sum, round_power);
    
    if ( !sp_receipt->round_check_sum ) return check_sum;
    
    double round_power = sp_receipt->round_power;
    
    if ( check_sum <= round_power ) return check_sum;
    
    //printf("check_sum / round_power = %f \n", check_sum / round_power);
    
    int count = 0;
    if ( sp_receipt->round_type == BETTER_ROUND ) count = Round(check_sum/round_power);
    if ( sp_receipt->round_type == LESS_ROUND )  count = floor(check_sum/round_power);
    if ( sp_receipt->round_type == GREATER_ROUND ) count = ceil(check_sum/round_power);   
    
    //printf("count = %d \n", count);
    //printf("round_power * count = %f \n", round_power * count);
    
    round_sum = check_sum - (round_power * count);
    
    return (round_power * count);
}
//==================================================================================// 
// сумма позиций (исключая сторнированные) без учета скидок
double Receipt::GetPositionSum()
{
   double s = 0;
   for ( vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++ )  {
     if ( i->enable_flag )    {
    	 s = s + i->GetSum();
     }
   }
	sum = Round(s*100)/100;
	return sum;
}
//==================================================================================// 
// итоговая сумма всех скидок
double Receipt::GetAllDiscountSum()
{
   double s = 0;
   for ( vector<Record_Receipt>::iterator i = records.begin(); i != records.end(); i++ )  {
     if ( i->enable_flag )    {
    	 s = s + i->GetDSum();
     }
   }
    
   
   //printf("sum of position discount - %f \n", s);
   //printf("sum of check reduction = %f \n", dsum);
   //printf("sum of round_sum = %f \n", round_sum);
   
   GetResultSum();
   
   s = s + dsum + round_sum;
   //printf("disount result sum = %f \n", s);
	sum = Round(s*100)/100;
   
	return sum;
}
//==================================================================================// 
